class_name GameConnector

extends Object

var proto_factory = preload("res://script/protobuf/proto_factory.gd")
var struct_factory = preload("res://script/struct/struct_factory.gd")

var _url: String
var _sock: WebSocketPeer
var _send_buffer: StreamPeerBuffer
var _state: int
var _message_handlers = {}

func _init():
	_sock = WebSocketPeer.new()
	_send_buffer = StreamPeerBuffer.new()
	_state = _sock.get_ready_state()

func connect_to_url(url):
	_url = url
	var err = _sock.connect_to_url(_url)
	if err != OK:
		print("connect_to_url _url = ", _url)
		return false

	_sock.set_no_delay(true)
	return true

func is_struct_msg(msg_id):
	return (msg_id & 0x8000) > 0

func is_connecting():
	return _sock.get_ready_state() == WebSocketPeer.STATE_CONNECTING

func is_open():
	return _sock.get_ready_state() == WebSocketPeer.STATE_OPEN

func is_closing():
	return _sock.get_ready_state() == WebSocketPeer.STATE_CLOSING

func is_closed():
	return _sock.get_ready_state() == WebSocketPeer.STATE_CLOSED

func send_data(msg):
	if not is_open():
		print("socket state err url = ", _url, ", state = ", _sock.get_ready_state())
		return
	var msg_id = proto_factory.get_proto_id(msg)
	if not msg_id:
		print("msg_id is not found msg_id= ", msg_id, ", msg = ", msg)
		return
	_send_buffer.put_u16(msg_id)
	_send_buffer.put_data(msg.to_bytes())
	_flush_send_buffer()

func on_message(msg_cls, obj, handler):
	add_listener(msg_cls.proto_name, Callable(obj, handler))

func add_listener(event_name, handler):
	if not _message_handlers.has(event_name):
		_message_handlers[event_name] = []
	_message_handlers[event_name].append(handler)

func del_listener(event_name, handler):
	if not _message_handlers.has(event_name):
		return
	var handlers = _message_handlers[event_name]
	var size = handlers.size()

	var del_list = []
	for i in range(size, 0, -1):
		if handler == handlers[i - 1]: 
			del_list.append(i - 1)
	
	for index in del_list:
		handlers.ease(index)

func call_listener(event_name, msg):
	if not _message_handlers.has(event_name):
		print("call_listener event_name = ", event_name)
		return
	for handler in _message_handlers[event_name]:
		handler.call(msg)

func close():
	_sock.close()
	
func process():
	if not _url:
		return
	_sock.poll()
	var state = _sock.get_ready_state()
	if state == WebSocketPeer.STATE_OPEN:
		if _state != state:
			_on_connected()
		_start_recv()
	elif state == WebSocketPeer.STATE_CLOSED:
		if _state != state:
			_on_disconnected()
	_state = state


func _on_connected():
	call_listener("connected", null)

func _on_disconnected():
	call_listener("disconnected", null)

func _start_recv():
	while _sock.get_available_packet_count():
		var packet = _sock.get_packet()
		if packet.size() < 0:
			continue
		_on_recv_data(packet)

func _on_recv_data(packet: PackedByteArray):
	var msg_id = packet.decode_u16(0)
	var msg 
	if is_struct_msg(msg_id):
		msg = struct_factory.create_struct_msg(msg_id)
	else:
		msg = proto_factory.create_proto_msg(msg_id)
	if not msg:
		print("msg not found msg_id = ", msg_id, ", is_struct_msg = ", is_struct_msg)
		return
	msg.from_bytes(packet, 2)
	call_listener(msg.proto_name, msg)

func _flush_send_buffer():
	var send_err = _sock.send(_send_buffer.data_array,
		WebSocketPeer.WRITE_MODE_BINARY)
	if send_err != OK:
		printerr("socket send err url = ", _url, ", send_err = ", send_err)
	_send_buffer.seek(0)
